/**
 * Copyright 2009 Henning Teek, Ingo Renner
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.bricket.plugin.user.repository.jpa.impl;

import java.util.ArrayList;
import java.util.List;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.security.authentication.dao.SaltSource;
import org.springframework.security.authentication.encoding.PasswordEncoder;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import brix.plugins.springsecurity.BrixUserProvider;

import com.googlecode.bricket.plugin.mail.repository.MailService;
import com.googlecode.bricket.plugin.user.domain.Role;
import com.googlecode.bricket.plugin.user.domain.User;
import com.googlecode.bricket.plugin.user.domain.Role.Roles;
import com.googlecode.bricket.plugin.user.repository.UserService;
import com.googlecode.bricket.plugin.user.repository.jpa.RoleDao;
import com.googlecode.bricket.plugin.user.repository.jpa.UserDao;

@Service(value="userService")
@Transactional
public class UserDetailsServiceImpl implements UserService, UserDetailsService, BrixUserProvider {
	private final static Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class);

	@Autowired
	private PasswordEncoder passwordEncoder;

	@Autowired
	private SaltSource saltSource;

	@Autowired
	private MailService mailService;

	@Resource
	private UserDao userDao;

	@Resource
	private RoleDao roleDao;

    private static int randomInt(int min, int max) {
        return (int)(Math.random() * (max - min) + min);
    }

    private static String randomString(int min, int max) {
        int num = randomInt(min, max);
        byte b[] = new byte[num];
        for (int i = 0; i < num; i++) {
            b[i] = (byte)randomInt('a', 'z');
        }
        return new String(b);
    }

    @Override
    public void init() {
    	if (roleDao.count().longValue() == 0) {
    		for (Roles role : Roles.values()) {
        		Role r = new Role();
        		r.setName(role.name());
        		roleDao.save(r);
    			log.info("created auto generated role: " + r.getName());
			}
    	}

		if (userDao.count().longValue() == 0) {
			User admin = new User();
			admin.setEmail("admin@localhost");
			admin.setPassword("admin");
			admin.setFirstname("auto");
			admin.setLastname("generated");
			admin.setEnabled(true);
			admin.getRoles().add(roleDao.findByName(Roles.ROLE_ADMIN.name()));
			admin.getRoles().add(roleDao.findByName(Roles.ROLE_USER.name()));
			saveUser(admin);
			log.info("auto generated admin user created. Login: admin@localhost / Password: admin");
		}
	}

	@Override
	public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException, DataAccessException {
		final User user = userDao.findByEmail(email);
		if (user == null) {
			throw new UsernameNotFoundException("no user for email: " + email);
		}
		return new UserServiceImpl(user);
	}

	@Override
	public User saveUser(User user) {
		if (user.isNew()) {
			user = userDao.save(user);
			user.setPassword(passwordEncoder.encodePassword(user.getPassword(), saltSource.getSalt(new UserServiceImpl(user))));
			user.setActivationKey(randomString(10, 12));
			user.getRoles().add(roleDao.findByName(Roles.ROLE_USER.name()));
			log.info("creating new user: " + user);
		} else {
			final User u = userDao.findByEmail(user.getEmail());
			if (user.getPassword() == null || "".equals(user.getPassword())) {
				user.setPassword(u.getPassword());
				log.info("updating user: " + user);
			} else {
				user.setPassword(passwordEncoder.encodePassword(user.getPassword(), saltSource.getSalt(new UserServiceImpl(user))));
				log.info("updating user (new password): " + user);
			}
		}
		return userDao.save(user);
	}

	@Override
	public void deleteUser(User user) {
		userDao.delete(userDao.readByPrimaryKey(user.getId()));
	}

	@Override
	public void signupUser(User user) {
		User u = saveUser(user);

		if (!mailService.sendSignupMail(u)) {
			log.error("error sending activation mail. user will be enabled automatically.");
			u = userDao.findByEmail(u.getEmail());
			u.setEnabled(true);
			userDao.save(u);
		}
	}

	@Override
	public User loadUser(Long id) {
		return userDao.readByPrimaryKey(id);
	}

	@Override
	public List<User> loadAllUser() {
		return userDao.readAll();
	}

	@Override
	public List<GrantedAuthority> getAllAuthorities() {
		ArrayList<GrantedAuthority> res = new ArrayList<GrantedAuthority>();
		List<Role> roles = roleDao.readAll();
		for (Role role : roles) {
			res.add(new GrantedAuthorityImpl(role.getName()));
		}
		return res;
	}

	@Override
	public GrantedAuthority getGrantedAuthorityByID(String name) {
		return new GrantedAuthorityImpl(roleDao.findByName(name).getName());
	}
}
